`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2020/09/14 21:14:16
// Design Name: 
// Module Name: ArithmeticResult
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

`include "opcode.v"
`include "funct.v"
`include "rt.v"

module ArithmeticResult(
    input wire [31:0] i_rs_out,
    input wire [31:0] i_rt_out,
    input wire [31:0] i_inst_addr,
    input wire [31:0] i_inst,
    input wire [31:0] i_imm32_zero,
    input wire [31:0] i_imm32_sign,
    
    output wire [31:0] o_result
    );
    
    wire [5:0] op;
    wire [5:0] funct;
    wire [4:0] sa;
    wire [4:0] rt;
    assign op = i_inst[31:26];
    assign funct = i_inst[5:0];
    assign sa = i_inst[10:6];
    assign rt = i_inst[20:16];
    
    wire [31:0] leading_result;
    CountLeading leading(
        .control(funct == `FUNCT_CLO ? 1 : 0),
        .data(i_rs_out),
        .result(leading_result)
    );
    
    wire [31:0] special2_result;
    assign special2_result
        = leading_result
    ;
    
    wire [31:0] special_result;
    assign special_result
        = funct == `FUNCT_AND ? i_rs_out & i_rt_out
        : funct == `FUNCT_OR ? i_rs_out | i_rt_out
        : funct == `FUNCT_XOR ? i_rs_out ^ i_rt_out
        : funct == `FUNCT_NOR ? ~(i_rs_out | i_rt_out)
        : funct == `FUNCT_SLL ? i_rt_out << sa
        : funct == `FUNCT_SRL ? i_rt_out >> sa
        : funct == `FUNCT_SRA ? i_rt_out >>> sa
        : funct == `FUNCT_SLLV ? i_rt_out << i_rs_out[4:0]
        : funct == `FUNCT_SRLV ? i_rt_out >> i_rs_out[4:0]
        : funct == `FUNCT_SRAV ? $signed(i_rt_out) >>> i_rs_out[4:0]
        : funct == `FUNCT_ADD ? i_rs_out + i_rt_out // todo ec
        : funct == `FUNCT_ADDU ? i_rs_out + i_rt_out
        : funct == `FUNCT_SUB ? i_rs_out - i_rt_out // todo
        : funct == `FUNCT_SUBU ? i_rs_out - i_rt_out
        : funct == `FUNCT_SLT ? $signed(i_rs_out) < $signed(i_rt_out)
        : funct == `FUNCT_SLTU ? i_rs_out < i_rt_out
        : funct == `FUNCT_MOVN ? i_rs_out
        : funct == `FUNCT_MOVZ ? i_rs_out
        : funct == `FUNCT_JALR ? i_inst_addr + 8
        : 0
    ;
    
    wire [31:0] regimm_result;
    assign regimm_result
        = rt == `RT_BLTZAL ? i_inst_addr + 8
        : rt == `RT_BGEZAL ? i_inst_addr + 8
        : 0
    ;
//    wire [31:0] special2_result;
//    assign special2_result
//        = funct == `FUNCT_CLZ ? 
//        : funct == `FUNCT_CLO ?
    
    assign o_result
        = op == `OP_SPECIAL ? special_result
        : op == `OP_SPECIAL2 ? special2_result
        : op == `OP_REGIMM ? regimm_result
        : op == `OP_ANDI ? i_rs_out & i_imm32_zero
        : op == `OP_XORI ? i_rs_out ^ i_imm32_zero
        : op == `OP_LUI ? {i_inst[15:0], 16'h0000}
        : op == `OP_ADDI ? i_rs_out + i_imm32_sign // todo
        : op == `OP_ADDIU ? i_rs_out + i_imm32_sign
        : op == `OP_SLTI ? $signed(i_rs_out) < $signed(i_imm32_sign)
        : op == `OP_SLTIU ? i_rs_out < i_imm32_sign
        : op == `OP_LW ? i_rs_out + i_imm32_sign
        : op == `OP_SW ? i_rs_out + i_imm32_sign
        : op == `OP_JAL ? i_inst_addr + 8
        : 0;
    
endmodule
